<?php
/**
 * $Id: common.php 27 2009-06-27 02:26:17Z eyou80 $
 */

/**
 * Tinyask公共类
 *
 * @package Common
 */
class Tinyask_Common
{
	const VERSION = '0.1/9.6.4';

	/**
	 * 默认配置
	 *
	 * @var array
	 */
	public static $config = array(
		'autoload' => true,
		'exception' => true,
		'timezone' => 'UTC',
		'charset' => 'UTF-8',
		'gzip' => true,
		'contentType'   =>  'text/html'
	);

	/**
	 * 初始化环境
	 *
	 * @param array $config
	 */
	public static function init(array $config = array())
	{
		self::$config = empty($config) ? self::$config : array_merge(self::$config, $config);

		/** 自动载入 **/
		if(self::$config['autoload']) {
			function __autoload($className)
			{
				require_once str_replace('_', '/', strtolower($className)) . '.php';
			}
		}

		/** 兼容php6 */
		if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
			$_GET = self::stripslashes($_GET);
			$_POST = self::stripslashes($_POST);
			$_COOKIE = self::stripslashes($_COOKIE);
			$_REQUEST = self::stripslashes($_REQUEST);

			reset($_GET);
			reset($_POST);
			reset($_COOKIE);
			reset($_REQUEST);
		}

		if(!ini_get('date.timezone') && function_exists('date_default_timezone_set')) {
			@date_default_timezone_set(self::$config['timezone']);
		}

		/**  输出监视 **/
		if(self::$config['gzip'] && !empty($_SERVER['HTTP_ACCEPT_ENCODING'])
		&& strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false) {
			ob_start("ob_gzhandler");
		} else {
			ob_start();
		}

		/** 设置异常处理机制 **/
		set_exception_handler(array('Tinyask_Common', 'exceptionHandler'));

		if(isset(self::$config['contentType'])) {
			Tinyask_Response::setContentType(self::$config['contentType'], self::$config['charset']);
		}
	}

	/**
	 * 字符串截取
	 *
	 * @param string $string
	 * @param integer $start
	 * @param integer $length
	 * @param string $trim
	 * @return string
	 */
	public static function substr($string, $start, $length, $trim = '...')
	{
		if(function_exists('mb_get_info')) {
			$realLength = mb_strlen($string, self::$config['charset']);
			$string = mb_substr($string, $start, $length, self::$config['charset']);
			return ($length < $realLength - $start) ? $string . $trim : $string;
		} else {
			$matches = array();
			preg_match_all("/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/", $string, $matches);
			$string = join('', array_slice($matches[0], $start, $length));
			return ($length < count($matches[0]) - $start) ? $string . $trim : $string;
		}
	}

	/**
	 * 获取字符串字数
	 *
	 * @param string $string
	 * @return integer
	 */
	public static function strlen($string)
	{
		if(function_exists('mb_get_info')) {
			return mb_strlen($string, self::$config['charset']);
		} else {
			$matches = array();
			preg_match_all("/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/", $string, $matches);
			return count($matches[0]);
		}
	}

	/**
	 * 深度去除转义
	 *
	 * @param mixed $value
	 * @return mixed
	 */
	public static function stripslashes($value)
	{
		return is_array($value) ? array_map(array('Tinyask_Common', 'stripslashes'), $value) : stripslashes($value);
	}

	/**
	 *  将路径转化为链接
	 *
	 * @param string $path
	 * @param string $prefix
	 * @return string
	 */
	public static function url($path, $prefix)
	{
		$path = strpos($path, './') === 0 ? substr($path, 2) : $path;
		return rtrim($prefix, '/') . '/'. str_replace('//', '/', ltrim($path, '/'));
	}

	/**
	 * sql搜索过滤器
	 *
	 * @param string $string
	 * @return string
	 */
	public static function searchFilter($string)
	{
		return str_replace(array('%', '?', '*', '/', '{', '}'), '', $string);
	}

	/**
	 * xss过滤器
	 *
	 * @param string $string
	 * @return string
	 */
	public static function xssFilter($string)
	{
		return $string;
	}

	/**
	 * url过滤器
	 *
	 * @param string $url
	 * @return string
	 */
	public static function urlFilter($url)
	{
		$params = parse_url(str_replace(array("\r", "\n"), '', $url));
		if(isset($params['scheme'])) {
			if(!in_array($params['scheme'], array('http', 'https'))) {
				return false;
			}
		}

		$params = array_map(array('Tinyask_Common', 'removeUrlXss'), $params);
		return self::buildUrl($params);
	}

	/**
	 * 根据parse_url的结果重构url地址
	 *
	 * @param array $params
	 * @return string
	 */
	public static function buildUrl(array $params)
	{
		return (isset($params['scheme']) ? $params['scheme'] . '://' : null)
		. (isset($params['user']) ? $params['user'] . (isset($params['pass']) ? ':' . $params['pass'] : null) . '@' : null)
		. (isset($params['host']) ? $params['host'] : null)
		. (isset($params['port']) ? ':' . $params['port'] : null)
		. (isset($params['path']) ? $params['path'] : null)
		. (isset($params['query']) ? '?' . $params['path'] : null)
		. (isset($params['fragment']) ? '#' . $params['fragment'] : null);
	}

	/**
	 * 解密字符串, 应用的是dz的authcode算法
	 *
	 * @param string $str
	 * @param string $key 密钥
	 * @return string
	 */
	public static function decode($str, $key = '')
	{
		$ckeyLen = 4;
		$key = md5($key ? $key : Tic_Config::get()->mixedStr);
		$keya = md5(substr($key, 0, 16));
		$keyb = md5(substr($key, 16, 16));
		$keyc = $ckeyLen ? substr($str, 0, $ckeyLen) : '';

		$cryptkey = $keya . md5($keya . $keyc);
		$keyLen = strlen($cryptkey);

		$str = base64_decode(substr($str, $ckeyLen));
		$strLen = strlen($str);

		$result = '';
		$box = range(0, 255);

		$rndkey = array();
		for($i = 0; $i <= 255; $i++)
		{
			$rndkey[$i] = ord($cryptkey[$i % $keyLen]);
		}

		for($j = $i = 0; $i < 256; $i++)
		{
			$j = ($j + $box[$i] + $rndkey[$i]) % 256;
			$tmp = $box[$i];
			$box[$i] = $box[$j];
			$box[$j] = $tmp;
		}

		for($a = $j = $i = 0; $i < $strLen; $i++)
		{
			$a = ($a + 1) % 256;
			$j = ($j + $box[$a]) % 256;
			$tmp = $box[$a];
			$box[$a] = $box[$j];
			$box[$j] = $tmp;
			$result .= chr(ord($str[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
		}

		if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16))
		{
			return substr($result, 26);
		}
		else
		{
			return '';
		}
	}

	/**
	 * 加密字符串, 应用的是dz的authcode算法
	 *
	 * @param string $str
	 * @param string $key 密钥
	 * @param int $expiry 有效期(秒)
	 * @return string
	 */
	public static function encode($str, $key = '', $expiry = 0)
	{
		$ckeyLen = 4;
		$key = md5($key ? $key : Tic_Config::get()->mixedStr);
		$keya = md5(substr($key, 0, 16));
		$keyb = md5(substr($key, 16, 16));
		$keyc = $ckeyLen ? substr(md5(microtime()), -$ckeyLen) : '';

		$cryptkey = $keya . md5($keya . $keyc);
		$keyLen = strlen($cryptkey);

		$str = sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($str . $keyb), 0, 16) . $str;
		$strLen = strlen($str);

		$result = '';
		$box = range(0, 255);

		$rndkey = array();
		for($i = 0; $i <= 255; $i++)
		{
			$rndkey[$i] = ord($cryptkey[$i % $keyLen]);
		}

		for($j = $i = 0; $i < 256; $i++)
		{
			$j = ($j + $box[$i] + $rndkey[$i]) % 256;
			$tmp = $box[$i];
			$box[$i] = $box[$j];
			$box[$j] = $tmp;
		}

		for($a = $j = $i = 0; $i < $strLen; $i++)
		{
			$a = ($a + 1) % 256;
			$j = ($j + $box[$a]) % 256;
			$tmp = $box[$a];
			$box[$a] = $box[$j];
			$box[$j] = $tmp;
			$result .= chr(ord($str[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
		}
		return $keyc . str_replace('=', '', base64_encode($result));
	}

	/**
	 * 生成随机字符串或数字
	 *
	 * @param int $length
	 * @param bool $numeric
	 * @return string
	 */
	public static function random($length, $numeric = false)
	{
		if($numeric)
		{
			$str = sprintf('%0' . $length . 'd', mt_rand(0, pow(10, $length) - 1));
		}
		else
		{
			$chars = '0123456789abcdefghijklmnopqrstuvwxyz';
			$max = 35;

			/** 生成以字母开头的随机字符串 **/
			$str = $chars[mt_rand(9, $max)];
			for($i = 1; $i < $length; $i++)
			{
				$str .= $chars[mt_rand(0, $max)];
			}
		}
		return $str;
	}

	/**
	 * 异常处理
	 *
	 * @param Exception $exception
	 */
	public static function exceptionHandler(Exception $exception)
	{
		if(self::$config['exception']) {
			echo nl2br($exception->__toString());
		} else {
			self::error($exception);
		}

		exit;
	}

	/**
	 * 错误信息输出
	 *
	 * @param mixed $exception
	 */
	public static function error($exception)
	{
		$charset = self::$config['charset'];
		$isException = is_object($exception);

		if($isException) {
			$code = $exception->getCode();
			$message = $exception->getMessage();
		} else {
			$code = $exception;
		}

		echo
<<<EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset={$charset}" />
    <title>{$code}</title>
    <style type="text/css">
        body {
            background: #f7fbe9;
            font-family: "Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana;
        }
        #error {
            background: #333;
            width: 360px;
            margin: 0 auto;
            margin-top: 100px;
            color: #fff;
            padding: 10px;

            -moz-border-radius-topleft: 4px;
            -moz-border-radius-topright: 4px;
            -moz-border-radius-bottomleft: 4px;
            -moz-border-radius-bottomright: 4px;
            -webkit-border-top-left-radius: 4px;
            -webkit-border-top-right-radius: 4px;
            -webkit-border-bottom-left-radius: 4px;
            -webkit-border-bottom-right-radius: 4px;

            border-top-left-radius: 4px;
            border-top-right-radius: 4px;
            border-bottom-left-radius: 4px;
            border-bottom-right-radius: 4px;
        }
        h1 {
            padding: 10px;
            margin: 0;
            font-size: 36px;
        }
        p {
            padding: 0 20px 20px 20px;
            margin: 0;
            font-size: 12px;
        }

    </style>
</head>
<body>
    <div id="error">
        <h1>{$code}</h1>
        <p>{$message}</p>
    </div>
</body>
</html>
EOF;
	}
}
